import logging

from django.db import transaction
from django.db.models import Q, F, Sum
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response

from BYD_ERP.utils.base_views import MultipleDestroyMixin, MultipleAuditMixin
from BYD_ERP.utils.pagination import GlobalPagination
from erp_system.models import UserModel
from basic_info.models import CustomerModel
from goods_info.models import GoodsInventoryModel
from warehouse_info.serializer.out_storage_serializer import OutStorageSerializer, OutStorageGetSerializer

from warehouse_info.models import OutStorageModel

logger = logging.getLogger('my')


class OutStorageView(viewsets.ModelViewSet, MultipleDestroyMixin, MultipleAuditMixin):
    """
        create:
        出库单--新增，注意：其中images_list="1,2,3,4"；里面是附件的ID

        出库单新增，status：201（成功），return：新增出库单信息

        destroy:
        出库单--删除

        出库单删除，status：204（成功），return：None

        multiple_delete:
        出库单--批量删除，必传参数：ids=[1,2,3,4,...]

        出库单批量删除，status：204（成功），return：None

        update:
        出库单--修改，注意：其中images_list="1,2,3,4"；里面是附件的ID

        出库单修改，status：200（成功），return：修改后的出库单信息

        partial_update:
        出库单--局部修改，可以传任意属性的值，服务器会修改指定的属性值

        出库单局部修改，status：200（成功），return：修改后的出库单信息

        list:
        出库单列表

        出库单列表信息，status：200（成功），return：出库单列表

        retrieve:
        根据ID查询某一个出库单

        查询指定ID的出库单，status：200（成功），return：指定出库单

    """
    queryset = OutStorageModel.objects.all()
    serializer_class = OutStorageSerializer
    pagination_class = GlobalPagination

    def get_serializer_class(self):
        if self.action == 'retrieve' or self.action == 'find':
            return OutStorageGetSerializer
        return OutStorageSerializer

    def get_queryset(self):
        if self.action == 'find':  # 过滤查询
            # 获取请求参数（在json中）：
            number_code = self.request.data.get('number_code', None)  # 单据编号
            keyword = self.request.data.get('keyword', None)
            start_date = self.request.data.get('start_date', None)
            end_date = self.request.data.get('end_date', None)
            customer = self.request.data.get('customer', 0)
            operator_user = self.request.data.get('operator_user', 0)
            sales_man = self.request.data.get('sales_man', 0)
            status = self.request.data.get('status', 0)

            remark = self.request.data.get('remark', None)
            warehouse = self.request.data.get('warehouse', 0)
            account = self.request.data.get('account', 0)
            sale_order_number_code = self.request.data.get('sale_order_number_code', None)

            query = Q()
            if keyword:
                child_query = Q()
                child_query.add(Q(item_list__name__contains=keyword), 'OR')
                child_query.add(Q(item_list__specification__contains=keyword), 'OR')
                child_query.add(Q(item_list__model_number__contains=keyword), 'OR')
                query.add(child_query, 'AND')
            if start_date:
                query.add(Q(invoices_date__gt=start_date), 'AND')
            if end_date:
                query.add(Q(invoices_date__lt=end_date), 'AND')
            if customer:
                query.add(Q(customer__id__contains=customer), 'AND')
            if operator_user:
                query.add(Q(operator_user=operator_user), 'AND')
            if sales_man:
                query.add(Q(sales_man=sales_man), 'AND')
            if number_code:
                query.add(Q(number_code__contains=number_code), 'AND')  # 单据编号
            if status:
                query.add(Q(status=status), 'AND')
            if remark:
                query.add(Q(remark__contains=remark), 'AND')
            if warehouse:
                query.add(Q(item_list__warehouse_id=warehouse), 'AND')
            if account:
                query.add(Q(account__id=account), 'AND')
            if sale_order_number_code:
                query.add(Q(sale_order_number_code__contains=sale_order_number_code), 'AND')

            # distinct()做去重。
            return OutStorageModel.objects.filter(query).distinct().all()
        else:
            return OutStorageModel.objects.all()

    params = openapi.Schema(type=openapi.TYPE_OBJECT, properties={
        'keyword': openapi.Schema(type=openapi.TYPE_STRING, description="名称的关键字或件号、型号"),
        'start_date': openapi.Schema(type=openapi.TYPE_STRING, description="起始日期2024-2-24"),
        'end_date': openapi.Schema(type=openapi.TYPE_STRING, description="结束日期2024-2-24"),
        'status': openapi.Schema(type=openapi.TYPE_STRING, description="状态0或者1,2,3,4,5"),
        'customer': openapi.Schema(type=openapi.TYPE_INTEGER, description="顾客的ID"),
        'operator_user': openapi.Schema(type=openapi.TYPE_INTEGER, description="制单人/操作用户的ID"),
        'sales_man': openapi.Schema(type=openapi.TYPE_INTEGER, description="业务代表/经办人的ID"),
        'number_code': openapi.Schema(type=openapi.TYPE_STRING, description="单据编号"),
        'remark': openapi.Schema(type=openapi.TYPE_STRING, description="备注/摘要"),

        'warehouse': openapi.Schema(type=openapi.TYPE_INTEGER, description="仓库的ID"),
        'account': openapi.Schema(type=openapi.TYPE_INTEGER, description="结算账户的ID"),
        'sale_order_number_code': openapi.Schema(type=openapi.TYPE_STRING, description="销售订单号"),
    })

    # 分页参数必须是query_param
    page_param = openapi.Parameter(name='page', in_=openapi.IN_QUERY, description="页号", type=openapi.TYPE_STRING)
    size_param = openapi.Parameter(name='page_size_user_defined', in_=openapi.IN_QUERY, description="每页显示的数量",
                                   type=openapi.TYPE_STRING)

    @swagger_auto_schema(method='post', request_body=params, manual_parameters=[page_param, size_param],
                         operation_description="出库单的过滤查询")
    # swagger_auto_schema中如果method='get'， 是不能用request_body=open_ids的
    # request_body就是定义json格式的传参
    @action(methods=['post'], detail=False)
    def find(self, request, *args, **kwargs):
        return super(OutStorageView, self).list(request=request, *args, **kwargs)


    body_json = openapi.Schema(type=openapi.TYPE_OBJECT, required=['ids', 'user_id', 'is_audit'], properties={
        'ids': openapi.Schema(type=openapi.TYPE_ARRAY, items=openapi.Schema(type=openapi.TYPE_INTEGER),
                              description="选择那些需要审核/反审核的ID（主键）列表"),
        'user_id': openapi.Schema(type=openapi.TYPE_INTEGER, description="审核人/审核用户的ID"),
        'is_audit': openapi.Schema(type=openapi.TYPE_STRING, description="是否审核，审核：1，反审核：0"),
    })

    @swagger_auto_schema(method='put', request_body=body_json, operation_description="批量审核/反审核")
    @action(methods=['put'], detail=False)
    @transaction.atomic  # 自动数据库事务，就是说同时执行成功，或者同时不执行成功
    def multiple_audit(self, request, *args, **kwargs):
        audit_ids = request.data.get('ids')
        user_id = request.data.get('user_id')
        is_audit = request.data.get('is_audit', '1')

        if not audit_ids:
            return Response(data={'detail': '参数错误，ids为必传参数'}, status=status.HTTP_400_BAD_REQUEST)
        if not isinstance(audit_ids, list):
            # 如果传过来的参数不是列表
            return Response(data={'detail': '参数错误，ids必须是列表类型'}, status=status.HTTP_400_BAD_REQUEST)

        queryset = self.get_queryset()
        # self就是当前这个对象
        # 所有需要批量审核的出库单列表
        aud_out_list = queryset.filter(id__in=audit_ids).all()

        if aud_out_list.count() != len(audit_ids):
            return Response(data={'detail': '审核的数据不存在'}, status=status.HTTP_400_BAD_REQUEST)

        # 加上all()才能变成列表
        for item in aud_out_list:  # item代表一个单据（出库单）
            if item.status != '1' and is_audit == '0':
                # 出库单状态只有为1的时候才能进行反审核操作
                return Response(data={'detail': '不能进行反审核操作，因为当前单据是未审核状态或已经生效'}, status=status.HTTP_400_BAD_REQUEST)

            if item.status != '0' and is_audit == '1':
                # 出库单状态只有为0的时候才能进行审核操作
                return Response(data={'detail': '不能进行审核操作，因为当前单据已经审核过'}, status=status.HTTP_400_BAD_REQUEST)

        # sale_order销售订单的状态，0：未审核，1：已审核，2：部分出库，3：全部出库，4：收取定金，5：完成销售（此订单货全部出库，全款已收）
        # out_storage出库单的状态，0：未审核，1：已审核，2：部分收款，3：收款完成（包含欠款）

        # 用户的ID
        if not user_id:
            return Response(data={'detail': '参数错误，user_id为必传参数'}, status=status.HTTP_400_BAD_REQUEST)

        # 审批用户
        check_user = UserModel.objects.get(id=int(user_id))

        # aud_out_list待审核的出库单列表
        # out_storage待审核的出库单
        for out_storage in aud_out_list:

            # 一、销售出库单进行审核的条件判断，就是说什么情况下才能进行审核。
            #    1.销售出库单状态status必须是0，就是未审核状态
            #    2.是否关联了销售订单，要求销售订单的状态必须是这三种，1：已审核，2：部分出库，4：已收定金，否则出库单无法审核
            if out_storage.status != '0':
                return Response(data={'detail': '不能进行出库审核，因为当前出库单已审核过'}, status=status.HTTP_400_BAD_REQUEST)
            if out_storage.status == '0' and out_storage.sale_order:
                if out_storage.sale_order.status != '1' and out_storage.sale_order.status != '2' and out_storage.sale_order.status != '4':
                    return Response(data={'detail': '不能进行出库审核，因为关联的销售订单未生效或者已全部出库或者销售订单已完成'},
                                    status=status.HTTP_400_BAD_REQUEST)

            # 关于：库存不足的问题
            # 以下编程还不行，需要用一个for循环，后期优化一下
            # if out_storage.status == '0' and (out_storage.item_list.out_count > out_storage.item_list.cur_inventory):
            #     return Response(data={'detail': '不能进行出库审核，因为当前库存不足'}, status=status.HTTP_400_BAD_REQUEST)

            # sale_order销售订单的状态，0：未审核，1：已审核，2：部分出库，3.全部出库，4：已收定金，5：完成销售（此订单货全部出库，全款已收）


            # 二、审核后必须要同时顺带完成的工作事项
            #    1.如果有欠款，修改对应客户的末期应收current_receivable
            #    2.修改关联的销售订单的状态，部分出库或者全部出库（类同于：选单模式）
            #    3.相关货品的库存需要减少：原来的库存 -= 当前出库数量
            print('---------出库单类型--------')
            print(out_storage.outbound_category)
            # outbound_category出库类型，1：委托发货单，2：生产领料出库，3：样品等领料出库，4：库存返工出库
            if out_storage.customer and out_storage.this_debt > 0:
                # 修改顾客的期末应收+=本次欠款
                # F("current_receivable")获取原有的某个字段current_receivable当前应付的值
                CustomerModel.objects.filter(id=out_storage.customer.id).update(
                    current_receivable=F('current_receivable') + out_storage.this_debt)
            # 2.修改关联的销售订单的状态，部分出库或者全部出库（类同于：选单模式）
            if out_storage.sale_order:
                # 如果有关联的销售订单
                # 查询该销售订单，已经出库的数量out_stored_count
                # exclude(status='0')排除函数，就是说状态不为0。
                out_stored_count_dict = OutStorageModel.objects.filter(sale_order_id=out_storage.sale_order_id).exclude(
                    status='0').aggregate(sum=Sum('number_count'))
                out_stored_count = out_stored_count_dict['sum'] if out_stored_count_dict['sum'] else 0
                # if not out_stored_count.get('sum', 0):
                #     out_stored_count['sum'] = 0
                # 如果：对应销售订单之前出库数量 + 当前出库数量 == 对应销售订单总数量
                if (out_stored_count + out_storage.number_count) == out_storage.sale_order.number_count:
                    # 修改对应销售订单状态为：3全部出库
                    out_storage.sale_order.status = '3'
                elif (out_stored_count + out_storage.number_count) < out_storage.sale_order.number_count:
                    # 修改对应销售订单状态为：2部分出库
                    out_storage.sale_order.status = '2'
                else:
                    return Response(data={'detail': '不能进行出库审核，因为出库数量已超出对应销售订单总数'},
                                    status=status.HTTP_400_BAD_REQUEST)
                out_storage.sale_order.save()
            # 3.相关货品的库存需要减少：原来的库存 -= 当前出库数量
            if out_storage.item_list.exists():
                for item in out_storage.item_list.all():
                    GoodsInventoryModel.objects.filter(goods_id=item.goods_id, warehouse_id=item.warehouse_id).update(
                        cur_inventory=F('cur_inventory') - item.out_count)

        # 进行审核操作
        self.get_queryset().filter(id__in=audit_ids).update(status=is_audit, check_user_fir_name=check_user.real_name,
                                                            check_user_fir_id=check_user.id)

        return Response(status=status.HTTP_200_OK)
